'''
KML Data Classes
================

These classes implement the elements contain the data attributes of KML objects

Created on Sep 28, 2010

@author: morgan
'''

from __xml__ import createElement, xml
from __string__ import T
from sys import maxint
from base import KMLObject, Coordinate, Angles
from enum import refreshMode, viewRefreshMode, altitudeMode

__all__ = ['coordinates', 'Location', 'Orientation', 'Link', 'Scale', 'Icon', 
           'Alias', 'ResourceMap', 'hotSpot', 'Snippet']

class coordinates(KMLObject):
    def __init__(self, value=[]):
        self.__tagName__ = "coordinates"
        KMLObject.__init__(self, id="")
        if value:
            self._coords = value
        else:
            self._coords = []
            
    def __str__(self):
        coordStr = ""
        if len(self._coords) == 3 and type(self._coords[0]) in [int, float]:
            self._coords = [self._coords]
        for coord in self._coords:
            lon, lat, alt = coord
            coords = str(lon) + "," + str(lat) + "," + str(alt)+ " "
            coordStr = " ".join([coordStr, coords])
        return coordStr
    
    def __repr__(self):
        d=dict(M=self.__module__, cls=self.__class__.__name__, 
               value=str(self.value))
        return T("$M.$cls($value)").substitute(d)


    def __len__(self):
        return len(self._coords)
    
    def __getitem__(self, index):
        if isinstance(index, slice):
            return self._coords[index.start:index.stop:index.step]
        else:
            return self._coords[index]
        
    @property
    def value(self):
        return self._coords
    
    @value.setter
    def value(self, value):
        self._coords = value
    
    @value.deleter
    def value(self, value):
        del self._coords
        
    def append(self,coord):
        if isinstance(coord, Coordinate):
            self._coords.append(coord.coordinates)
        elif isinstance(coord, coordinates):
            self._coords.append(list(coord.coordinates))
        elif type(coord) not in [list,tuple]:
            raise TypeError("Expecting list/tuple of length 3")
        elif type(coord) in [list,tuple] and len(coord) != 3:
            raise Exception("list/tuple must have a length of 3")
        else:
            self._coords.append(coord)
            
    def remove(self,coordOrIndex):
        if type(coordOrIndex) == tuple:
            self._coords = list(coordOrIndex)
        if type(coordOrIndex) == list:
            self._coords.remove(coordOrIndex)
        if type(coordOrIndex) == int:
            if coordOrIndex >= len(self._coords) or coordOrIndex < 0:
                raise ValueError("Index out of bounds")
            del self._coords[coordOrIndex]

    def parseKMLNode(self, KMLNode):
        self._coords = []
        KMLObject.parseKMLNode(self, KMLNode)
        coords = str(KMLNode.childNodes[0].data).strip().split(" ")
        for x in range(0,len(coords)):
            if coords[x]:
                coords[x] = [float(num) for num in coords[x].split(",")]
                if len(coords[x]) == 2:
                    coords[x].append(0)
                self._coords.append(coords[x])
    
    def toXmlNode(self,kml):
        return createElement(kml._doc,"coordinates", str(self))

class Location(KMLObject, Coordinate):
    def __init__(self, longitude=0, latitude=0, altitude=0, coords=None):
        self.__tagName__ = "Location"
        KMLObject.__init__(self)
        Coordinate.__init__(self, longitude, latitude, altitude, coords)
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "longitude", self.longitude))
        node.appendChild(createElement(kml._doc, "latitude", self.latitude))
        node.appendChild(createElement(kml._doc, "altitude", self.altitude))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("longitude")
        if elems:
            self.longitude = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("latitude")
        if elems:
            self.latitude = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("altitude")
        if elems:
            self.altitude = float(elems[0].childNodes[0].data)
        
    
class Orientation(KMLObject):
    def __init__(self, heading=0, tilt=0, roll=0):
        self.__tagName__ = "Orientation"
        KMLObject.__init__(self)
        self.heading = heading
        self.tilt = tilt
        self.roll = roll
        
    def __str__(self):
        d=dict(head=self.heading, tilt=self.tilt, roll=self.roll)
        return T("heading=$lon, tilt=$lat, roll=$alt").substitute(d)
    
    def __repr__(self):
        d=dict(module=self.__module__, head=self.heading, tilt=self.tilt, 
               roll=self.roll, cls=self.__class__.__name__)
        return T("$module.$cls($head, $tilt, $roll)").substitute(d)
    @property
    def heading(self):
        return self._heading
    
    @heading.setter
    def heading(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0 or value > 360:
            raise ValueError("Expecting value to be in the range of -180..+180.")
        else:
            self._heading = float(value)
            
    @property
    def tilt(self):
        return self._tilt
    
    @tilt.setter
    def tilt(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0 or value > 180:
            raise ValueError("Expecting value to be in the range of 0..180.")
        else:
            self._tilt = float(value)
            
    @property
    def roll(self):
        return self._roll
    
    @roll.setter
    def roll(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0 or value > 180:
            raise ValueError("Expecting value to be in the range of 0..180.")
        else:
            self._roll = float(value)
            
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "heading", self.heading))
        node.appendChild(createElement(kml._doc, "tilt", self.tilt))
        node.appendChild(createElement(kml._doc, "roll", self.roll))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("heading")
        if elems:
            self.heading = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("tilt")
        if elems:
            self.tilt = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("roll")
        if elems:
            self.roll = float(elems[0].childNodes[0].data)
            
class Link(KMLObject):
    def __init__(self, id="", href="", refreshMode=refreshMode.default,
                 refreshInterval=4, viewRefreshMode=viewRefreshMode.default,
                 viewRefreshInterval=4, viewBoundScale=1, viewFormat="",
                 httpQuery=""):
        self.__tagName__ = "Link"
        KMLObject.__init__(self, id)
        self.href = href
        self.refreshMode = refreshMode
        self.refreshInterval = refreshInterval
        self._viewRefreshMode = viewRefreshMode
        self.viewRefreshMode = viewRefreshMode
        self.viewRefreshInterval = viewRefreshInterval
        self.viewBoundScale = viewBoundScale
        self.viewFormat = viewFormat
        self.httpQuery = httpQuery
    
    @property
    def href(self):
        return self._href
    
    @href.setter
    def href(self, value):
        if type(value) != str:
            raise ValueError("String value expected")
        else:
            self._href = value        
            
    @property
    def refreshInterval(self):
        return self._refreshInterval

    @refreshInterval.setter
    def refreshInterval(self, value):
        if not type(value) in [int, float]:
            raise TypeError("Expecting value to an int or float.")
        elif (value < 0):
            raise ValueError("Expecting value to be positive.")
        else:
            self._refreshInterval = int(value)
            
    @property 
    def refreshMode(self):
        return self._refreshMode
    
    @refreshMode.setter
    def refreshMode(self, value):
        if type(value) != str and not(isinstance(value, refreshMode)):
            raise TypeError("Expecting value to be a string or a "+\
                            "kml.refreshMode instance")
        if type(value) == str:
            mode = refreshMode()
            mode.actualMode = value
            self._refreshMode = mode.actualMode
        else:
            self._refreshMode = value.actualMode
    
            
    @property
    def viewRefreshMode(self):
        return self._viewRefreshMode
    
    @viewRefreshMode.setter
    def viewRefreshMode(self, value):
        if type(value) != str and type(value) != viewRefreshMode:
            raise TypeError("Expecting value to be a string or a "+
                            "kml.viewRefreshMode instance.")
        if type(value) == str:
            mode = viewRefreshMode()
            mode.actualMode = value
            self._viewRefreshMode = mode.actualMode
        else:
            self._viewRefreshMode = value.actualMode
   
    @property
    def viewRefreshInterval(self):
        return self._viewRefreshInterval

    @viewRefreshInterval.setter
    def viewRefreshInterval(self, value):
        if not type(value) in [int, float]:
            raise TypeError("Expecting value to an int or float.")
        elif (value < 0):
            raise ValueError("Expecting value to be positive.")
        else:
            self._viewRefreshInterval = int(value)    
            
    @property
    def viewBoundScale(self):
        return self._viewBoundScale

    @viewBoundScale.setter
    def viewBoundScale(self, value):
        if not type(value) in [int, float]:
            raise TypeError("Expecting value to an int or float.")
        elif (value < 0):
            raise ValueError("Expecting value to be positive.")
        else:
            self._viewBoundScale = value
            
    @property
    def viewFormat(self):
        return self._viewFormat
    
    @viewFormat.setter
    def viewFormat(self, value):
        if not type(value) == str:
            raise TypeError("Expecting value to be a string.")
        else:
            self._viewFormat = value
            
            
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        if self.href:
            node.appendChild(createElement(kml._doc, "href", self.href))
        if self.refreshMode != refreshMode.default:
            mode = refreshMode(self.refreshMode)
            node.appendChild(mode.toXmlNode(kml))
        if self.refreshInterval != 4:
            node.appendChild(createElement(kml._doc, "refreshInterval",
                                           self.refreshInterval))
        if self.viewRefreshMode != viewRefreshMode.default:
            mode = viewRefreshMode(self.viewRefreshMode)
            node.appendChild(mode.toXmlNode(kml))
        
        if self.viewRefreshInterval != 4:
            node.appendChild(createElement(kml._doc, "viewRefreshInterval", 
                                           self.viewRefreshInterval))
        if self.viewBoundScale != 1:
            node.appendChild(createElement(kml._doc, "viewBoundScale",
                                           self.viewBoundScale))
        if self.viewFormat:
            node.appendChild(createElement(kml._doc, "viewFormat", 
                                           self.viewFormat))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("href")
        if elems:
            self.href=elems[0].childNodes[0].data
        elems = KMLNode.getElementsByTagName("refreshInterval")
        if elems:
            self.refreshInterval=int(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("refreshMode")
        if elems:
            self.refreshMode = elems[0].childNodes[0].data
        elems = KMLNode.getElementsByTagName("viewRefreshInterval")
        if elems:
            self._viewRefreshInterval = int(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("viewRefreshMode") 
        if elems:
            self.viewRefreshMode = elems[0].childNodes[0].data
        elems = KMLNode.getElementsByTagName("viewBoundScale")
        if elems:
            self.viewBoundScale = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("viewFormat")
        if elems:
            self.viewFormat = elems[0].childNodes[0].data

class Scale(KMLObject):
    def __init__(self, x=0, y=0, z=0):
        self.__tagName__ = "Scale"
        KMLObject.__init__(self)
        self.x = x
        self.y = y
        self.z = z
        
    @property
    def x(self):
        return self._x
    
    @x.setter
    def x(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0:
            raise ValueError("Expecting value to be positive.")
        else:
            self._x = float(value)
            
    @property
    def y(self):
        return self._y
    
    @y.setter
    def y(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0:
            raise ValueError("Expecting value to be positive.")
        else:
            self._y = float(value)
            
    @property
    def z(self):
        return self._z
    
    @z.setter
    def z(self, value):
        if type(value) not in [int, float]:
            raise TypeError("Value is expected to be an int or a float.")
        elif value < 0:
            raise ValueError("Expecting value to be positive.")
        else:
            self._z = float(value)
            
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "x", self.x))
        node.appendChild(createElement(kml._doc, "y", self.y))
        node.appendChild(createElement(kml._doc, "z", self.z))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("x")
        if elems:
            self.x = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("y")
        if elems:
            self.y = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("z")
        if elems:
            self.z = float(elems[0].childNodes[0].data)
            
       
class Icon(Link):
    """
    Defines an image associated with an B{IconStyle} or B{Overlay}. The required
    I{href} property defines the location of the image to be used as the
    B{Overlay} or as the icon for the B{Placemark}. This location can either be
    on a local file system or a remote web server. The I{x, y, w} and
    I{h} properties are used to select one icon from an image that contains
    multiple icons (often referred to as an icon palette).
    """
    def __init__(self, id="", x=0, y=0, height=0, width=0, *args, **kwdargs):
        Link.__init__(self, *args, **kwdargs)
        self.__tagName__ = "Icon"
        self.x = x
        self.y = y
        self.height = height 
        self.width = width
        
    @property
    def x(self):
        return self._x
    
    @x.setter
    def x(self, value):
        if type(value) != int:
            raise TypeError("Value of x must be an integer.")
        elif value < 0:
            raise ValueError("Value of x must be positive.")
        else:
            self._x = value

    @property
    def y(self):
        return self._y
    
    @y.setter
    def y(self, value):
        if type(value) != int:
            raise TypeError("Value of y must be an integer.")
        elif value < 0:
            raise ValueError("Value of y must be positive.")
        else:
            self._y = value

    @property
    def height(self):
        return self._h
    
    @height.setter
    def height(self, value):
        if type(value) != int:
            raise TypeError("Value of h must be an integer.")
        elif value < 0:
            raise ValueError("Value of h must be positive.")
        else:
            self._h = value
            
    @property
    def width(self):
        return self._w
    
    @width.setter
    def width(self, value):
        if type(value) != int:
            raise TypeError("Value of w must be an integer.")
        elif value < 0:
            raise ValueError("Value of w must be positive.")
        else:
            self._w = value


    def toXmlNode(self,kml):
        myNode = Link.toXmlNode(self, kml)
        if self.x:
            myNode.appendChild(createElement(kml._doc, "x", self.x))
        if self.y:   
            myNode.appendChild(createElement(kml._doc, "y", self.y))
        if self.height:
            myNode.appendChild(createElement(kml._doc, "h", self.height))
        if self.width:
            myNode.appendChild(createElement(kml._doc, "w", self.width))
        return myNode

    def parseKMLNode(self,KMLNode):
        Link.parseKMLNode(self, KMLNode)
        if KMLNode.getElementsByTagName("href") != []:
            self.bgColor = str(KMLNode.getElementsByTagName("href")[0].\
                    childNodes[0].data)
        if KMLNode.getElementsByTagName("x"):
            self.x = int(KMLNode.getElementsByTagName("x")[0].\
                    childNodes[0].data)
        if KMLNode.getElementsByTagName("y"):
            self.y = int(KMLNode.getElementsByTagName("y")[0].\
                    childNodes[0].data)
        if KMLNode.getElementsByTagName("h"):
            self.height = int(KMLNode.getElementsByTagName("h")[0].\
                    childNodes[0].data)
        if KMLNode.getElementsByTagName("w"):
            self.width = int(KMLNode.getElementsByTagName("w")[0].\
                    childNodes[0].data)     

class Alias(KMLObject):
    def __init__(self, sourceHref="", targetHref=""):
        self.__tagName__ = "Alias"
        KMLObject.__init__(self)
        self._targetHref = targetHref
        self._sourceHref = sourceHref
        
    @property
    def targetHref(self):
        return self._targetHref

    @targetHref.setter
    def targetHref(self, value):
        if type(value) != str:
            raise TypeError("Expecting value to be a string.")
        else:
            self._targetHref = value
            
    @property
    def sourceHref(self):
        return self._sourceHref

    @sourceHref.setter
    def sourceHref(self, value):
        if type(value) != str:
            raise TypeError("Expecting value to be a string.")
        else:
            self._sourceHref = value
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "targetHref", self.targetHref))
        node.appendChild(createElement(kml._doc, "sourceHref", self.sourceHref))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("targetHref")
        if elems:
            self.targetHref = elems[0].childNodes[0].data
        elems = KMLNode.getElementsByTagName("sourceHref")
        if elems:
            self.sourceHref = elems[0].childNodes[0].data

class ResourceMap(KMLObject):
    def __init__(self, *args):
        self.__tagName__ = "ResourceMap"
        KMLObject.__init__(self)
        for arg in args:
            self.addAlias(arg)
        self._aliases = []
    
    def addAlias(self, value):
        if type(value) != Alias:
            raise ValueError("Expecting an Alias object")
        else:
            self._aliases.append(value)
    
    def removeAlias(self, aliasOrIndex):
        if type(aliasOrIndex) == Alias or type(aliasOrIndex) == int:
            self._aliases.remove(aliasOrIndex)
    
    def clearAliases(self):
        self._aliases = []
    
    @property
    def Aliases(self):
        return self._aliases
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        for alias in self.Aliases:
            node.appendChild(alias.toXmlNode(kml))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("Alias")
        for elem in elems:
            alias = Alias()
            alias.parseKMLNode(elem)
            self.addAlias(alias)
    
class hotSpot(KMLObject):
    def __init__(self, x=0.5, y=0.5, xunits="fraction", yunits="fraction"):
        self.__tagName__ = "hotSpot"
        KMLObject.__init__(self, id="")
        self.xunits = xunits
        self.yunits = yunits
        self.x = x
        self.y = y
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        if self.xunits == "fraction":
            minValue = 0
            maxValue = 1
            validTypes = [int, float]
        elif self.xunits == "insetPixels":
            minValue = 0
            maxValue = maxint
            validTypes = [int]
        else:
            raise Exception("This exception should never occur.")
        if not type(value) in validTypes:
            raise TypeError("Expecting x to be one of %s" % str(validTypes))
        elif value < minValue or value > maxValue:
            raise ValueError("Expecting x to be in the range of %d to %d" \
                            % (minValue, maxValue))
        else:
            self._x = value
            
    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, value):
        if self.yunits == "fraction":
            minValue = 0
            maxValue = 1
            validTypes = [int, float]
        elif self.yunits == "insetPixels":
            minValue = 0
            maxValue = maxint
            validTypes = [int]
        else:
            raise Exception("This exception should never occur.")
        if not type(value) in validTypes:
            raise TypeError("Expecting y to be one of %s" % str(validTypes))
        elif value < minValue or value > maxValue:
            raise ValueError("Expecting y to be in the range of %d to %d" \
                            % (minValue, maxValue))
        else:
            self._y = value      
            
    @property
    def xunits(self):
        return self._xunits
    
    @property
    def yunits(self):
        return self._yunits
    
    @xunits.setter
    def xunits(self, value):
        if type(value) != str:
            raise TypeError("Expecting xunits to be a string.")
        elif value not in ["fraction", "insetPixels"]:
            raise ValueError("Expecting xunits to be one of 'fraction' or 'insetPixels'.")
        else:
            self._xunits = value
        
    @yunits.setter
    def yunits(self, value):
        if type(value) != str:
            raise TypeError("Expecting yunits to be a string.")
        elif value not in ["fraction", "insetPixels"]:
            raise ValueError("Expecting yunits to be one of 'fraction' or 'insetPixels'.")
        else:
            self._yunits = value
    
    @property
    def xfraction(self):
        if self.xunits == "fraction":
            return True
        else:
            return False
        
    @xfraction.setter
    def xfraction(self, value):
        if type(value) != bool:
            raise TypeError("Expecting xfraction to be True or False")
        else:
            if value:
                self.xunits = "fraction"
            else:
                self.xunits = "insetPixels"
    @property
    def yfraction(self):
        if self.yunits == "fraction":
            return True
        else:
            return False
        
    @yfraction.setter
    def yfraction(self, value):
        if type(value) != bool:
            raise TypeError("Expecting yfraction to be True or False")
        else:
            if value:
                self.yunits = "fraction"
            else:
                self.yunits = "insetPixels"
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.setAttribute("x", str(self.x))
        node.setAttribute("y", str(self.y))
        node.setAttribute("xunits", self.xunits)
        node.setAttribute("yunits", self.yunits)
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        self.xunits = KMLNode.getAttribute("xunits")
        self.yunits = KMLNode.getAttribute("yunits")
        if self.xfraction:
            self.x = float(KMLNode.getAttribute("x"))
        else:
            self.x = int(KMLNode.getAttribute("x"))
        if self.yfraction:
            self.y = float(KMLNode.getAttribute("y"))
        else:
            self.y = int(KMLNode.getAttribute("y"))
        return None
    
class Snippet(KMLObject):
    def __init__(self, value="", maxLines="2"):
        self.__tagName__ = "Snippet"
        KMLObject.__init__(self)
        self._maxLines = maxLines
        self._snippet = value
        self._cdata = None
        self._setValue(value)

    def _setValue(self, value):
        if not value:
            self._snippet = ""
            self._cdata = None
        elif type(value) == str:
            self._snippet = value
        elif isinstance(value, xml.CDATASection):
            self._snippet=value.toxml("UTF-8")
            self._cdata=value
        else:
            raise TypeError("Snippet was expecting a value of string or"+\
                            " xml.dom.minidom.CDATASection")        
        
    def _getValue(self):
        return self._snippet
    
    value = property(_getValue, _setValue)
    
    def _setMaxLines(self, value):
        if type(value) != str:
            raise TypeError("Snippet is expecting a maxLines value of integer")
        elif (value < 0):
            raise ValueError("Snippet is expecting a positive maxLines value")
        else:
            self._maxLines = value
            
    def _getMaxLines(self):
        return self._maxLines    

    maxLines = property(_setMaxLines, _getMaxLines)

    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        if KMLNode.getAttribute("maxLines"):
            self._maxLines = KMLNode.getAttribute("maxLines")
        self._snippet = str(KMLNode.childNodes[0].data)
        
    def toXmlNode(self,kml):
        node = createElement(kml._doc, self.__tagName__, self.value)
        if self._maxLines != 2:
            node.setAttribute("maxLines", self._maxLines)
        return node

class LatLonAltBox(KMLObject):
    def __init__(self, north=0, south=0, east=0, west=0, minAltitude=0, 
                 maxAltitude=0, altitudeMode=altitudeMode.clampToGround):
        self.__tagName__ = "LatLonAtlBox"
        KMLObject.__init__(self, id="")
        self.north = north
        self.south = south
        self.east = east
        self.west = west
        self.minAltitude = minAltitude
        self.maxAltitude = maxAltitude
        self.altitudeMode = altitudeMode
    
    @property
    def north(self):
        return self._north
    
    @north.setter
    def north(self, value):
        if type(value) in [float, int]:
            if value <= -90 and value >= 90:
                self._north = float(value)
            else:
                raise ValueError("north must be between -90 and 90, inclusive.")
        else:
            raise TypeError("north must be an integer or a float")
    
    @property
    def south(self):
        return self._north
    
    @south.setter
    def south(self, value):
        if type(value) in [float, int]:
            if value <= -90 and value >= 90:
                self._south = float(value)
            else:
                raise ValueError("south must be between -90 and 90, inclusive.")
        else:
            raise TypeError("south must be an integer or a float")    
    
    @property
    def east(self):
        return self._east
    
    @east.setter
    def east(self, value):
        if type(value) in [float, int]:
            if value <= -180 and value >= 180:
                self._east = float(value)
            else:
                raise ValueError("east must be between -180 and 180, inclusive.")
        else:
            raise TypeError("east must be an integer or a float")        
        
    @property
    def west(self):
        return self._west
    
    @west.setter
    def west(self, value):
        if type(value) in [float, int]:
            if value <= -180 and value >= 180:
                self._west = float(value)
            else:
                raise ValueError("west must be between -180 and 180, inclusive.")
        else:
            raise TypeError("west must be an integer or a float")       
        
    @property
    def minAltitude(self):
        return self._minAltitude

    @minAltitude.setter
    def minAltitude(self, value):
        if type(value) in [float, int]:
            self._minAltitude = float(value)
        else:
            raise TypeError("minAltitude must be an integer or a float.")
    
    @property
    def maxAltitude(self):
        return self._maxAltitude
       
    @maxAltitude.setter
    def maxAltitude(self, value):
        if type(value) in [float, int]:
            self._maxAltitude = float(value)
        else:
            raise TypeError("maxAltitude must be an integer or a float.")
        
    @property
    def altitudeMode(self):
        return self._altitudeMode
    
    @altitudeMode.setter
    def altitudeMode(self, value):
        if type(value) is str:
            self._altitudeMode = str(altitudeMode(value))
        elif type(value) is altitudeMode:
            self._altitudeMode = altitudeMode.value
        else:
            raise TypeError("altitudeMode must be a kml.AltitudeMode instance, a kml.altitudeMode class attribute, or a string.")
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "north", self.north))
        node.appendChild(createElement(kml._doc, "south", self.south))
        node.appendChild(createElement(kml._doc, "east", self.east))
        node.appendChild(createElement(kml._doc, "west", self.west))
        if self.minAltitude:
            node.appendChild(createElement(kml._doc, "minAltitude", 
                                           self.minAltitude))
        if self.maxAltitude:
            node.appendChild(createElement(kml._doc, "maxAltitude", 
                                           self.maxAltitude))
        if self.altitudeMode != 'clampToGround':
            altMode = altitudeMode(self.altitudeMode)
            node.appendChild(altMode.toXmlNode(kml))
        return node

    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("north")
        if elems:
            self.north = float(elems[0].childNodes[0].data)
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("south")
        if elems:
            self.south = float(elems[0].childNodes[0].data)
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("east")
        if elems:
            self.west = float(elems[0].childNodes[0].data)
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("west")
        if elems:
            self.east = float(elems[0].childNodes[0].data)
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("minAltitude")
        if elems:
            self.minAltitude = float(elems[0].childNodes[0].data)
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("maxAltitude")
        if elems:
            self.maxAltitude = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("altitudeMode")
        if elems:
            altMode = altitudeMode()
            altMode.parseKMLNode(elems[0])
            self.altitudeMode = altMode
            
class Lod(KMLObject):
    """
    Lod is an abbreviation for Level of Detail. <Lod> describes the size of 
    the projected region on the screen that is required in order for the region 
    to be considered "active." Also specifies the size of the pixel ramp used 
    for fading in (from transparent to opaque) and fading out (from opaque 
    to transparent)
    """
    def __init__(self, minLodPixels=256, maxLodPixels=-1, minFadeExtent=0, 
                 maxFadeExtent=0):
        self.__tagName__ = "Lod"
        KMLObject.__init__(self, id="")
        self.minLodPixels=minLodPixels
        self.maxLodPixels=maxLodPixels
        self.minFadeExtent=minFadeExtent
        self.maxFadeExtent=maxFadeExtent
    
    @property
    def minLodPixels(self):
        """
        Measurement in screen pixels that represents the minimum limit of the 
        visibility range for a given Region. Google Earth calculates the size 
        of the Region when projected onto screen space. Then it computes the 
        square root of the Region's area (if, for example, the Region is square 
        and the viewpoint is directly above the Region, and the Region is not 
        tilted, this measurement is equal to the width of the projected Region). 
        If this measurement falls within the limits defined by <minLodPixels> 
        and <maxLodPixels> (and if the <LatLonAltBox> is in view), the Region 
        is active. If this limit is not reached, the associated geometry is 
        considered to be too far from the user's viewpoint to be drawn.
        """
        return self._minLodPixels
    
    @minLodPixels.setter
    def minLodPixels(self, value):
        if type(value) in [float, int]:
            self._minLodPixels = float(value)
        else:
            raise TypeError("minLodPixels must be an integer or a float")
    
    @property
    def maxLodPixels(self):
        """
        Measurement in screen pixels that represents the maximum limit of the 
        visibility range for a given Region. A value of −1, the default, 
        indicates "active to infinite size."
        """
        return self._maxLodPixels
    
    @maxLodPixels.setter
    def maxLodPixels(self, value):
        if type(value) in [float, int]:
            self._maxLodPixels = float(value)
        else:
            raise TypeError("maxLodPixels must be an integer or a float")
    
    @property
    def minFadeExtent(self):
        """
        Distance over which the geometry fades, from fully opaque to fully 
        transparent. This ramp value, expressed in screen pixels, is applied at 
        the minimum end of the LOD (visibility) limits.
        """
        return self._minFadeExtent
    
    @minFadeExtent.setter
    def minFadeExtent(self, value):
        if type(value) in [float, int]:
            self._minFadeExtent = float(value)
        else:
            raise TypeError("minFadeExtent must be an integer or a float")
        
    @property
    def maxFadeExtent(self):
        """ 
        Distance over which the geometry fades, from fully transparent to fully 
        opaque. This ramp value, expressed in screen pixels, is applied at the 
        maximum end of the LOD (visibility) limits.
        """
        return self._maxFadeExtent
    
    @minFadeExtent.setter
    def minFadeExtent(self, value):
        if type(value) in [float, int]:
            self._maxFadeExtent = float(value)
        else:
            raise TypeError("maxFadeExtent must be an integer or a float")
    
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "minLodPixels", 
                                       self.minLodPixels))
        node.appendChild(createElement(kml._doc, "maxLodPixels", 
                                       self.maxLodPixels))
        node.appendChild(createElement(kml._doc, "minFadeExtent", 
                                       self.minFadeExtent))
        node.appendChild(createElement(kml._doc, "maxFadeExtent", 
                                       self.maxFadeExtent))
        return node
        
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("minLodPixels")
        if elems:
            self.minLodPixels = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("minLodPixels")
        if elems:
            self.maxLodPixels = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("minFadeExtent")
        if elems:
            self.minFadeExtent = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("maxFadeExtent")
        if elems:
            self.maxFadeExtent = float(elems[0].childNodes[0].data)

class Region(KMLObject):
    def __init__(self, id="", LatLonAltBox=None, Lod=None):
        self.__tagName__ = "Region"
        KMLObject.__init__(self, id)
        self.LatLonAltBox=LatLonAltBox
        self.Lod=Lod
    
    @property
    def LatLonAltBox(self):
        if hasattr(self, "_latLonAltBox"):
            return self._latLonAltBox
        else:
            return None
        
    @LatLonAltBox.setter
    def LatLonAltBox(self, value):
        if value is None:
            del self._latLonAltBox
        elif type(value) is LatLonAltBox:
            self._latLonAltBox = value
        else:
            raise TypeError("LatLonAltBox must be a kml.LatLonAltBox instance or None.")
    
    @property
    def Lod(self):
        if hasattr(self, "_lod"):
            return self._lod
        else:
            return None

    @Lod.setter
    def Lod(self, value):
        if value is None:
            del self._lod
        elif type(value) is Lod:
            self._lod = value
        else:
            raise TypeError("Lod must be a kml.Lod instance or None.")
        
    def toXmlNode(self, kml):
        node = KMLObject.toXmlNode(self, kml)
        if self.LatLonAltBox:
            node.appendChild(self.LatLonAltBox.toXmlNode(kml))
        else:
            raise AttributeError("You must define LatLonBox first!")
        if self.Lod:
            node.appendChild(self.Lod.toXmlNode(kml))
        return node
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("LatLonAltBox")
        if elems:
            llaBox = LatLonAltBox()
            llaBox.parseKMLNode(elems[0])
            self.LatLonAltBox = llaBox
        elems = KMLNode.getElementsByTagName("Lod")
        if elems:
            lod = Lod()
            lod.parseKMLNode(elems[0])
            self.Lod = lod

        
    
        
 
        
    
    
